预备知识
1.延迟加载导入的概念及作用
延迟加载导入表和导入表示相互分离的,延迟加载导入表是特殊的导入表,和导入表不同的是,延迟加载导入表所记录的dll不会被操作系统加载,只有在函数被应用程序调用的时候,PE中注册的延迟加载函数才会根据延迟加载导入表的记录,动态加载dll,以及修正导入函数的VA。
2.延迟加载的优势
延迟加载由于没有在程序初始化的时候初始化dll,只是会在应用程序调用某个模块的时候加载该模块,所以使用延迟加载技术的程序拥有更高的初始化速度。
通过延迟加载的方法,提高了程序的兼容性,原因在于基于延迟加载,所用到的模块不需要在初始化前加载,保证了程序能够运行成功,如果缺少模块或者缺少模
块里面的函数造成的异常,可以使得编译器单独处理该某个dll或者某个函数的调用。
PE中的延迟加载导入表
1.延迟加载导入表的定位
延迟加载导入表的描述信息在数目目录的第14个目录项中,定位方法和前面的导入导出表一致。
2.延迟加载描述符IMAGE_DELAY_IMPORT_DESCRIPTOR
IMAGE_DELAY_IMPORT_DESCRIPTOR的结构如下:
- Attributes:双字,暂时未用到,
- RVA_DLLName:双字,延迟加载dll名称的字符串的地址。
- RVA_ModuleHandle:双字,延迟加载dll的句柄的地址。
- RVA_DelayIAT: 延迟加载导入地址表的RVA
- RVA_DelayINT:延迟加载导入名称表的RVA
- RVA_BoundIAT:延迟绑定导入地址表的RVA,延迟绑定导入地址表是由IMAGE_THUNK_DATA组成的数组。他和最后一项dwTimeStamp用于最后的绑定阶段。
- RVA_UnloadIAT:延迟卸载导入地址表由IMAGE_THUNK_DATA组成的数组,程序使用它来卸载dll(包括函数),所用到的参数是原来IAT的精确副本。做释放处理需要做一下几个工作
- 1)释放函数/dll
- 2)ModuleHandle清零
- 3)使用UIAT覆盖IAT。
3.详解延迟加载机制
使用延迟加载技术的程序,链接器会做一下几个事情:
- 1)将函数_delayLoadHelper嵌入到可执行模块
- 2)删除可执行文件导入表的相关内容,避免在初始化时候显式加载dll
- 3)构造PE相关信息,以便_delayLoadHelper正确的延迟加载函数。
- 4)调用_delayLoadHelper函数加载dll或获取调用函数地址。
在_delayLoadHelper函数中,还调用了LoadLibrary加载dll,调用了GetprocessAddress获取函数地址,调用了FreeLibrary去释放dll。
4.利用windbg查看延迟加载导入表
我们首先用windows xp下的explorer程序举例子。
- 查看程序加载的模块:lm m explorer
- 显示PE文件头:!dh start_address -f
- 查看延迟加载导入的内存空间:dd start_address+offset
- 比如查看延迟加载导入表第二项的内容dll的ASCII:da address
4.延迟导入的两个问题
1)异常处理
通过延迟加载机制,未能成功加载的dll或者由于版本问题,未能成功调用的函数,函数_delayLoadHelper会抛出软件异常(其实是API函数剖出的异常)
2)dll卸载
不能使用FreeLibrary卸载dll,因为在函数_delayLoadHelper中存在卸载函数,